JBoss Community Archive (Read Only)

SwitchYard 0.7

Rules Services

The Rules Component is a pluggable container in SwitchYard which allows business rules to be exposed as a service.  One fronts their rules with a custom interface and, if desired, can easily annotate it's methods to define which should execute the rules.

Important

A Rules Service is a type of Knowledge Service (the other type being a BPM Service).  Thus, it is strongly suggested you familiarize yourself with the shared configuration capabilities of Knowledge Services.

Note

Configuration of SwitchYard's Rules Component has been overhauled greatly between 0.6 and 0.7.  Documentation on this page reflects 0.7.  The wiki article: "BPM & Rules Component Configuration Changes" is a useful resource to consult.

Providing a Service

To provide a service with the Rules component, you will have to:

  1. Define your rules. The Rules Component currently supports Drools as the rule engine.  Even though it is quite simple to write rules in Drools, that project's developer tooling and business analyst tooling are very mature.

  2. Create a java interface, fronting your rules, which can be exposed to other services, and/or to your binding(s).

  3. Add the component containing the implementation and service interface to the SwitchYard configuration. This step can be automated using annotations and the SwitchYard plugin, explained below.

Here is an example of the component section of the SwitchYard configuration:

<component name="MyService">
    <implementation.rules xmlns="urn:switchyard-component-rules:config:1.0">
        <actions>
            <action operation="process" type="EXECUTE"/>
        </actions>
        <manifest>
            <resources>
                <resource location="/org/switchyard/userguide/MyService.drl" type="DRL"/>
            </resources>
        </manifest>
    </implementation.rules>
    <service name="MyService">
        <interface.java interface="org.switchyard.userguide.MyService"/>
    </service>
</component>

The MyService interface can be as simple as this, with no SwitchYard-specific imports:

package org.switchyard.userguide;
public interface MyService {
    public void process(MyData data);
}

However, if you want to have the SwitchYard configuration for this section auto-generated for you, you can use annotations and the SwitchYard Maven Plugin.  To do this, you can either add SwitchYard-specific annotations to your interface directly, or (and this is the recommended approach) you can create a separate interface for this purpose. Let's do that:

package org.switchyard.userguide;
import org.switchyard.component.common.knowledge.annotation.Manifest;
import org.switchyard.component.common.knowledge.annotation.Resource;
import org.switchyard.component.rules.annotation.Execute;
import org.switchyard.component.rules.annotation.Rules;
@Rules(
    value=MyService.class,
    manifest=@Manifest(resources={
        @Resource(location="/org/switchyard/userguide/MyService.drl", type="DRL")
    })
)
public interface MyServiceRules extends MyService {
    @Execute @Override
    public void process(MyData data);
}

That will create the SwitchYard configuration you saw above!

A few important points:

  1. In the original switchyard xml, the <actions/> element is optional. If not specified, the behavior is to execute the rules.

  2. You will have to configure the switchyard maven plugin with the RulesSwitchYardScanner.

  3. You can list as many resources as the Drools knowledge session requires within the manifest.  For more information, please see the Manifest section of the Knowledge Services documentation.

  4. You don't have to specify the drl resource using annotations. You can create a switchyard.xml file with the drl defined (as shown in the first example), and the plugin will merge what it found via annotations with what you provided in a preliminary switchyard.xml file.

Stateless vs. Stateful Rules Execution

By default, service method invocation will create a new Drools knowledge session, execute it given the passed-in domain data, and then be cleanly disposed.

However, it is possible to configure SwitchYard so that a stateful knowledge session will be used.  Specifically, it will "stick around" spanning multiple invocations.  (Please visit the Drools documentation to learn more about stateless vs. stateful knowledge sessions.)  To do this, you use the @FireAllRules annotation instead of @Execute. Or similarly in the switchyard.xml, use the FIRE_ALL_RULES action type instead of EXECUTE.

There is also a new capability which allows you to insert facts into a stateful knowledge session without firing the rules (you might want to do that later).  In this case, use the @Insert annotation.  Or similarly in switchyard.xml, use the INSERT action type.

Mapping Global Variables

Mapping variables from your SwitchYard Exchange, Context or Message into Drools Globals can be done via expressions, the default (and only currently supported) type is MVEL.  This can be done via annotions:

@Execute(... globals={
    @Mapping(expression="exchange.serviceName.localPart", expressionType=ExpressionType.MVEL, variable="service"),
    @Mapping(expression="context['org.switchyard.messageId']", variable="messageId", contextScope=Scope.IN),
    @Mapping(expression="message.content", variable="payload")
})

public void process(MyData data);

or via XML:

<implementation.rules ...>
    <actions>
        <action operation="process" type="EXECUTE">
            <globals>
                <mapping expression="exchange.serviceName.localPart" expressionType="MVEL" variable="service"/>
                <mapping expression="context['org.switchyard.messageId']" variable="messageId" contextScope="IN"/>
                <mapping expression="message.content" variable="payload"/>
            </globals>
        </action>
    </actions>
    ...
</implementation.rules>

Your expression can use the variables exchange (org.switchyard.Exchange), context (org.switchyard.Context) or message (org.switchyard.Message).  You can see in the example above is that context can be accessed in the expression as a java.util.Map.  When accessing it as such, the default properties Scope is IN.  This can be overridden using the contextScope attribute by changing it to OUT or EXCHANGE.

Now you can use these global variables in your Drools DRL:

package example

global java.lang.String service
global java.lang.String messageId
global com.example.Payload payload

rule "Example"
    when
        ...
    then
        ...
        System.out.println("service: " + service + ", messageId: " + messageId + ", payload: " + payload);
end

Of course, in a more realistic scenario, the payload would be accessed in rule firing because it was inserted into the session directly by the RulesExchangeHandler, and thus evaluated (rather than being accessed as a global). See Mapping Facts below.

Mapping Facts

The default Object which is inserted into the rules engine as a fact is the SwitchYard Message's content.  However, this can be overridden by specifying your own fact mappings.

IMPORTANT: If you specify your own fact mappings, the SwitchYard Message content will not be inserted as a fact.  You will have to add a fact mapping with an expression of "message.content" to have it still included.

Mapping facts from your SwitchYard Exchange, Context or Message for insertion into Drools can be done just like Globals.  This can be done via annotions:

@Execute(... inputs={
    @Mapping(expression="context['myPropertyFact']", expressionType=ExpressionType.MVEL, contextScope=Scope.IN),
    @Mapping(expression="message.content.myNestedFact")
})

public void process(MyData data);

or via XML:

<implementation.rules ...>

    <actions>
        <action operation="process" type="EXECUTE">
            <inputs>
                <mapping expression="context['myPropertyFact']" expressionType="MVEL" contextScope="IN"/>
                <mapping expression="message.content.myNestedFact"/>
            </inputs>
        </action>
    </actions>
    ...
</implementation.rules>
Note
  1. There is no point in specifying the "variable" attribute of the mappings (as done for global mappings), as the result of each expression is inserted as a nameless fact.  For stateless execution, the full list of facts is passed to the StatelessKnowledgeSessions' execute(Iterable) method. For stateful execution, each fact is individually inserted into the StatefulKnowledgeSession.  And for Complex Event Processing, each fact is individually inserted into the WorkingMemoryEntryPoint.

  2. If the result of the mapping expression implements Iterable (for example, a Collection), then the result is iterated over, and each iteration is inserted as a separate fact, rather than the parent Iterable itself being inserted. This is not recursive behavior (i.e: it is only done once).

Complex Event Processing

Complex Event Processing, or "CEP", is an advanced topic, and can best be explained in the Drools Fusion documentation. What will be shown here in the SwitchYard documentation is how it can be configured via annotation:

...
import org.switchyard.component.rules.annotation.FireUntilHalt;
import org.switchyard.component.rules.annotation.Rules;

@Rules(...
    properties={
        @Property(name="drools.clockType", value="realtime"),
        @Property(name="drools.eventProcessingMode", value="stream",
        @Property(name="drools.maxThreads", value="1"),
        @Property(name="drools.multithreadEvaluation", value="false")
    }
)
public interface MyServiceRules extends MyService {
    @FireUntilHalt(id="FooStream")
    public void processFooMessage(FooMessage foo);
    @FireUntilHalt(id="BarStream")
    public void processBarMessage(BarMessage bar);
}

or via XML:

<implementation.rules ...>
    <actions>
        <action id="FooStream" operation="processFooMessage" type="FIRE_UNTIL_HALT"/>
        <action id="BarStream" operation="processBarMessage" type="FIRE_UNTIL_HALT"/>
    </actions>
    ...
    <properties>
        <property name="drools.clockType" value="realtime"/>
        <property name="drools.eventProcessingMode" value="stream"/>
        <property name="drools.maxThreads" value="1"/>
        <property name="drools.multithreadEvaluation" value="false"/>
    </properties>
</implementation.rules>

Auditing a Service

Please see the Listeners and Loggers sections found in the Knowledge Services documentation.

Consuming a Service

Please see the Channels section found in the Knowledge Services documentation.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 09:47:12 UTC, last content change 2013-01-30 04:58:39 UTC.